import * as Cesium from 'cesium';
const { defaultValue, BufferUsage, defined, ComponentDatatype, Geometry, Math: CesiumMath, IndexDatatype, VertexArray, RuntimeError, Buffer,
    //@ts-ignore
    AttributeType } = Cesium;
export default function fixVertexArray() {
    //@ts-ignore
    Cesium.VertexArray.fromGeometry = function (options: any) {
        //@ts-ignore
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        //>>includeStart('debug', pragmas.debug);
        Cesium.Check.defined("options.context", options.context);
        //>>includeEnd('debug');

        const context = options.context;
        //@ts-ignore
        const geometry = defaultValue(options.geometry, defaultValue.EMPTY_OBJECT);

        const bufferUsage = defaultValue(
            options.bufferUsage,
            BufferUsage.DYNAMIC_DRAW,
        );

        const attributeLocations = defaultValue(
            options.attributeLocations,
            //@ts-ignore
            defaultValue.EMPTY_OBJECT,
        );
        const interleave = defaultValue(options.interleave, false);
        const createdVAAttributes = options.vertexArrayAttributes;

        let name;
        let attribute;
        let vertexBuffer;
        const vaAttributes = defined(createdVAAttributes) ? createdVAAttributes : [];
        const attributes = geometry.attributes;

        if (interleave) {
            // Use a single vertex buffer with interleaved vertices.
            const interleavedAttributes = interleaveAttributes(attributes);
            if (defined(interleavedAttributes)) {
                vertexBuffer = Buffer.createVertexBuffer({
                    context: context,
                    //@ts-ignore
                    typedArray: interleavedAttributes.buffer,
                    usage: bufferUsage,
                });
                const offsetsInBytes = interleavedAttributes.offsetsInBytes;
                const strideInBytes = interleavedAttributes.vertexSizeInBytes;

                for (name in attributes) {
                    if (attributes.hasOwnProperty(name) && defined(attributes[name])) {
                        attribute = attributes[name];

                        if (defined(attribute.values)) {
                            // Common case: per-vertex attributes
                            vaAttributes.push({
                                index: attributeLocations[name],
                                vertexBuffer: vertexBuffer,
                                componentDatatype: attribute.componentDatatype,
                                componentsPerAttribute: attribute.componentsPerAttribute,
                                normalize: attribute.normalize,
                                offsetInBytes: offsetsInBytes[name],
                                strideInBytes: strideInBytes,
                            });
                        } else {
                            // Constant attribute for all vertices
                            vaAttributes.push({
                                index: attributeLocations[name],
                                value: attribute.value,
                                componentDatatype: attribute.componentDatatype,
                                normalize: attribute.normalize,
                            });
                        }
                    }
                }
            }
        } else {
            // One vertex buffer per attribute.
            for (name in attributes) {
                if (attributes.hasOwnProperty(name) && defined(attributes[name])) {
                    attribute = attributes[name];

                    let componentDatatype = attribute.componentDatatype;
                    if (componentDatatype === ComponentDatatype.DOUBLE) {
                        componentDatatype = ComponentDatatype.FLOAT;
                    }

                    let attrProps = {};
                    vertexBuffer = undefined;
                    if (defined(attribute.values)) {
                        vertexBuffer = Buffer.createVertexBuffer({
                            context: context,
                            typedArray: ComponentDatatype.createTypedArray(
                                componentDatatype,
                                attribute.values,
                            ),
                            usage: bufferUsage,
                        });

                        attrProps = {
                            index: attributeLocations[name],
                            vertexBuffer: vertexBuffer,
                            value: attribute.value,
                            componentDatatype: componentDatatype,
                            componentsPerAttribute: attribute.componentsPerAttribute,
                            normalize: attribute.normalize,
                        };
                    }

                    //if we already have a typedArray lets use it
                    if (defined(attribute.typedArray)) {
                        vertexBuffer = Buffer.createVertexBuffer({
                            context: context,
                            typedArray: attribute.typedArray,
                            usage: bufferUsage,
                        });

                        attrProps = {
                            index: attributeLocations[name],
                            vertexBuffer: vertexBuffer,
                            value: undefined,
                            componentDatatype: componentDatatype,
                            componentsPerAttribute: AttributeType.getNumberOfComponents(
                                attribute.type,
                            ),
                            normalize: attribute.normalized,
                            instanceDivisor: attribute.instanceDivisor,
                        };
                    }

                    vaAttributes.push(attrProps);
                }
            }
        }

        let indexBuffer;
        const indices = geometry.indices;
        if (defined(indices)) {
            if (
                Geometry.computeNumberOfVertices(geometry) >=
                CesiumMath.SIXTY_FOUR_KILOBYTES &&
                context.elementIndexUint
            ) {
                indexBuffer = Buffer.createIndexBuffer({
                    context: context,
                    typedArray: new Uint32Array(indices),
                    usage: bufferUsage,
                    indexDatatype: IndexDatatype.UNSIGNED_INT,
                });
            } else {
                indexBuffer = Buffer.createIndexBuffer({
                    context: context,
                    typedArray: new Uint16Array(indices),
                    usage: bufferUsage,
                    indexDatatype: IndexDatatype.UNSIGNED_SHORT,
                });
            }
        }
        //@ts-ignore
        return new VertexArray({
            context: context,
            attributes: vaAttributes,
            indexBuffer: indexBuffer,
        });
    };
}
//#region 原始代码
function interleaveAttributes(attributes: any) {
    let j;
    let name;
    let attribute;

    // Extract attribute names.
    const names = [];
    for (name in attributes) {
        // Attribute needs to have per-vertex values; not a constant value for all vertices.
        if (
            attributes.hasOwnProperty(name) &&
            defined(attributes[name]) &&
            defined(attributes[name].values)
        ) {
            names.push(name);

            if (attributes[name].componentDatatype === ComponentDatatype.DOUBLE) {
                attributes[name].componentDatatype = ComponentDatatype.FLOAT;
                attributes[name].values = ComponentDatatype.createTypedArray(
                    ComponentDatatype.FLOAT,
                    attributes[name].values,
                );
            }
        }
    }

    // Validation.  Compute number of vertices.
    let numberOfVertices: any;
    const namesLength = names.length;

    if (namesLength > 0) {
        numberOfVertices = computeNumberOfVertices(attributes[names[0]]);

        for (j = 1; j < namesLength; ++j) {
            const currentNumberOfVertices = computeNumberOfVertices(
                attributes[names[j]],
            );

            if (currentNumberOfVertices !== numberOfVertices) {
                throw new RuntimeError(
                    `${"Each attribute list must have the same number of vertices.  " +
                    "Attribute "
                    }${names[j]} has a different number of vertices ` +
                    `(${currentNumberOfVertices.toString()})` +
                    ` than attribute ${names[0]} (${numberOfVertices.toString()}).`,
                );
            }
        }
    }

    // Sort attributes by the size of their components.  From left to right, a vertex stores floats, shorts, and then bytes.
    names.sort(function (left, right) {
        return (
            //@ts-ignore
            ComponentDatatype.getSizeInBytes(attributes[right].componentDatatype) -
            //@ts-ignore
            ComponentDatatype.getSizeInBytes(attributes[left].componentDatatype)
        );
    });

    // Compute sizes and strides.
    let vertexSizeInBytes = 0;
    const offsetsInBytes: any = {};

    for (j = 0; j < namesLength; ++j) {
        name = names[j];
        attribute = attributes[name];

        offsetsInBytes[name] = vertexSizeInBytes;
        vertexSizeInBytes += computeAttributeSizeInBytes(attribute);
    }

    if (vertexSizeInBytes > 0) {
        // Pad each vertex to be a multiple of the largest component datatype so each
        // attribute can be addressed using typed arrays.
        //@ts-ignore
        const maxComponentSizeInBytes = ComponentDatatype.getSizeInBytes(
            attributes[names[0]].componentDatatype,
        ); // Sorted large to small
        const remainder = vertexSizeInBytes % maxComponentSizeInBytes;
        if (remainder !== 0) {
            vertexSizeInBytes += maxComponentSizeInBytes - remainder;
        }

        // Total vertex buffer size in bytes, including per-vertex padding.
        const vertexBufferSizeInBytes = numberOfVertices * vertexSizeInBytes;

        // Create array for interleaved vertices.  Each attribute has a different view (pointer) into the array.
        const buffer = new ArrayBuffer(vertexBufferSizeInBytes);
        const views: any = {};

        for (j = 0; j < namesLength; ++j) {
            name = names[j];
            //@ts-ignore
            const sizeInBytes = ComponentDatatype.getSizeInBytes(
                attributes[name].componentDatatype,
            );

            views[name] = {
                pointer: ComponentDatatype.createTypedArray(
                    attributes[name].componentDatatype,
                    //@ts-ignore
                    buffer,
                ),
                index: offsetsInBytes[name] / sizeInBytes, // Offset in ComponentType
                strideInComponentType: vertexSizeInBytes / sizeInBytes,
            };
        }

        // Copy attributes into one interleaved array.
        // PERFORMANCE_IDEA:  Can we optimize these loops?
        for (j = 0; j < numberOfVertices; ++j) {
            for (let n = 0; n < namesLength; ++n) {
                name = names[n];
                attribute = attributes[name];
                const values = attribute.values;
                const view = views[name];
                const pointer = view.pointer;

                const numberOfComponents = attribute.componentsPerAttribute;
                for (let k = 0; k < numberOfComponents; ++k) {
                    pointer[view.index + k] = values[j * numberOfComponents + k];
                }

                view.index += view.strideInComponentType;
            }
        }

        return {
            buffer: buffer,
            offsetsInBytes: offsetsInBytes,
            vertexSizeInBytes: vertexSizeInBytes,
        };
    }

    // No attributes to interleave.
    return undefined;
}

function computeNumberOfVertices(attribute: any) {
    return attribute.values.length / attribute.componentsPerAttribute;
}
function computeAttributeSizeInBytes(attribute: any) {
    return (
        //@ts-ignore
        ComponentDatatype.getSizeInBytes(attribute.componentDatatype) *
        attribute.componentsPerAttribute
    );
}
//#endregion